/*
 * Copyright 2024 ywcai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

`include "defines.v"
`timescale 1ns/1ps

module bus_arbiter(
	input	wire						clk,
	input	wire						rst_n,

	input	wire						m0_req_i,
    input	wire[`MemAddrBus]			m0_addr_i,

	input	wire						m1_req_i,
    input	wire[`MemAddrBus]			m1_addr_i,

	input	wire						m2_req_i,
    input	wire[`MemAddrBus]			m2_addr_i,

	input	wire						m3_req_i,
    input	wire[`MemAddrBus]			m3_addr_i,

	input	wire						transact_done_i,

	output	reg							m0_m_bus_grant_o,
	output	reg							m0_io_bus_grant_o,
	output	reg							m1_m_bus_grant_o,
	output	reg							m1_io_bus_grant_o,
	output	reg							m2_m_bus_grant_o,
	output	reg							m2_io_bus_grant_o,
	output	reg							m3_m_bus_grant_o,
	output	reg							m3_io_bus_grant_o
	);

	localparam IDLE = 2'b01;
	localparam BUSY = 2'b10;
	localparam OWNER_NONE = 3'b000;
	localparam OWNER_M0 = 3'b001;
	localparam OWNER_M1 = 3'b010;
	localparam OWNER_M2 = 3'b100;
	localparam OWNER_M3 = 3'b101;

	wire m0_m_req = (m0_addr_i >= `ROM_BASE & m0_addr_i < `UART_BASE)
		| (m0_addr_i >= `SDRAM_BASE & m0_addr_i < `ADDR_END);
	wire m0_io_req = (m0_addr_i <= `ADDR_END & (m0_addr_i < `ROM_BASE
		| (m0_addr_i >= `UART_BASE & m0_addr_i < `SDRAM_BASE)));

	wire m1_m_req = (m1_addr_i >= `ROM_BASE & m1_addr_i < `UART_BASE)
		| (m1_addr_i >= `SDRAM_BASE & m1_addr_i < `ADDR_END);
	wire m1_io_req = (m1_addr_i <= `ADDR_END & (m1_addr_i < `ROM_BASE
		| (m1_addr_i >= `UART_BASE & m1_addr_i < `SDRAM_BASE)));

	wire m2_m_req = (m2_addr_i >= `ROM_BASE & m2_addr_i < `UART_BASE)
		| (m2_addr_i >= `SDRAM_BASE & m2_addr_i < `ADDR_END);
	wire m2_io_req = (m2_addr_i <= `ADDR_END & (m2_addr_i < `ROM_BASE
		| (m2_addr_i >= `UART_BASE & m2_addr_i < `SDRAM_BASE)));

	wire m3_m_req = (m3_addr_i >= `ROM_BASE & m3_addr_i < `UART_BASE)
		| (m3_addr_i >= `SDRAM_BASE & m3_addr_i < `ADDR_END);
	wire m3_io_req = (m3_addr_i <= `ADDR_END & (m3_addr_i < `ROM_BASE
		| (m3_addr_i >= `UART_BASE & m3_addr_i < `SDRAM_BASE)));

	reg[2:0] owner;
	reg[1:0] cur_state, next_state;

    always @(posedge clk) begin
	    if (rst_n == `RESET_ENABLE) begin
			cur_state <= IDLE;
		end else begin
			cur_state <= next_state;
		end
	end

	always @(*) begin
		if (rst_n == `RESET_ENABLE) begin
			owner = OWNER_NONE;
			m0_m_bus_grant_o = `DISABLE;
			m0_io_bus_grant_o = `DISABLE;
			m1_m_bus_grant_o = `DISABLE;
			m1_io_bus_grant_o = `DISABLE;
			m2_m_bus_grant_o = `DISABLE;
			m2_io_bus_grant_o = `DISABLE;
			m3_m_bus_grant_o = `DISABLE;
			m3_io_bus_grant_o = `DISABLE;
			next_state = IDLE;
		end else begin
			case (cur_state)
				IDLE: begin
					case (owner)
						OWNER_M0, OWNER_NONE: begin
							if (m0_req_i) begin
								if (m0_m_req) begin
									m0_m_bus_grant_o = `ENABLE;
								end else begin
									m0_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M0;
								next_state = BUSY;
							end else if (m1_req_i) begin
								if (m1_m_req) begin
									m1_m_bus_grant_o = `ENABLE;
								end else begin
									m1_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M1;
								next_state = BUSY;
							end else if (m2_req_i) begin
								if (m2_m_req) begin
									m2_m_bus_grant_o = `ENABLE;
								end else begin
									m2_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M2;
								next_state = BUSY;
							end else if (m3_req_i) begin
								if (m3_m_req) begin
									m3_m_bus_grant_o = `ENABLE;
								end else begin
									m3_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M3;
								next_state = BUSY;
							end
						end

						OWNER_M1: begin
							if (m1_req_i) begin
								if (m1_m_req) begin
									m1_m_bus_grant_o = `ENABLE;
								end else begin
									m1_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M1;
								next_state = BUSY;
							end else if (m2_req_i) begin
								if (m2_m_req) begin
									m2_m_bus_grant_o = `ENABLE;
								end else begin
									m2_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M2;
								next_state = BUSY;
							end else if (m3_req_i) begin
								if (m3_m_req) begin
									m3_m_bus_grant_o = `ENABLE;
								end else begin
									m3_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M3;
								next_state = BUSY;
							end else if (m0_req_i) begin
								if (m0_m_req) begin
									m0_m_bus_grant_o = `ENABLE;
								end else begin
									m0_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M0;
								next_state = BUSY;
							end
						end

						OWNER_M2: begin
							if (m2_req_i) begin
								if (m2_m_req) begin
									m2_m_bus_grant_o = `ENABLE;
								end else begin
									m2_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M2;
								next_state = BUSY;
							end else if (m3_req_i) begin
								if (m3_m_req) begin
									m3_m_bus_grant_o = `ENABLE;
								end else begin
									m3_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M3;
								next_state = BUSY;
							end else if (m0_req_i) begin
								if (m0_m_req) begin
									m0_m_bus_grant_o = `ENABLE;
								end else begin
									m0_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M0;
								next_state = BUSY;
							end else if (m1_req_i) begin
								if (m1_m_req) begin
									m1_m_bus_grant_o = `ENABLE;
								end else begin
									m1_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M1;
								next_state = BUSY;
							end
						end

						OWNER_M3: begin
							if (m3_req_i) begin
								if (m3_m_req) begin
									m3_m_bus_grant_o = `ENABLE;
								end else begin
									m3_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M3;
								next_state = BUSY;
							end else if (m0_req_i) begin
								if (m0_m_req) begin
									m0_m_bus_grant_o = `ENABLE;
								end else begin
									m0_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M0;
								next_state = BUSY;
							end else if (m1_req_i) begin
								if (m1_m_req) begin
									m1_m_bus_grant_o = `ENABLE;
								end else begin
									m1_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M1;
								next_state = BUSY;
							end else if (m2_req_i) begin
								if (m2_m_req) begin
									m2_m_bus_grant_o = `ENABLE;
								end else begin
									m2_io_bus_grant_o = `ENABLE;
								end
								owner = OWNER_M2;
								next_state = BUSY;
							end
						end

						default: begin
							next_state = IDLE;
						end
					endcase
				end

				BUSY: begin
					if (transact_done_i) begin
						owner = OWNER_NONE;
						m0_m_bus_grant_o = `DISABLE;
						m0_io_bus_grant_o = `DISABLE;
						m1_m_bus_grant_o = `DISABLE;
						m1_io_bus_grant_o = `DISABLE;
						m2_m_bus_grant_o = `DISABLE;
						m2_io_bus_grant_o = `DISABLE;
						m3_m_bus_grant_o = `DISABLE;
						m3_io_bus_grant_o = `DISABLE;
						next_state = IDLE;
					end
				end

				default: begin
					next_state = IDLE;
				end
			endcase
		end
	end

endmodule
